/**
 * Project: dubbo.governance-2.2.0-SNAPSHOT
 * 
 * File Created at Mar 31, 2012
 * $Id: SyncUtils.java 184666 2012-07-05 11:13:17Z tony.chenl $
 * 
 * Copyright 1999-2100 Alibaba.com Corporation Limited.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * Alibaba Company. ("Confidential Information").  You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Alibaba.com.
 */
package com.alibaba.dubbo.governance.sync.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.utils.StringUtils;
import com.alibaba.dubbo.registry.common.domain.Cache;
import com.alibaba.dubbo.registry.common.domain.Consumer;
import com.alibaba.dubbo.registry.common.domain.Provider;
import com.alibaba.dubbo.registry.common.domain.Route;

/**
 * @author ding.lid
 */
public class SyncUtils {
    
    public static final String SERVICE_FILTER_KEY = ".service";

    public static final String ADDRESS_FILTER_KEY = ".address";
    
    public static final String ID_FILTER_KEY = ".id";

    public static Provider url2Provider(Pair<Long, URL> pair) {
    	if (pair == null) {
    		return null;
    	}
    	
        Long id = pair.getKey();
        URL url = pair.getValue();

        if (url == null)
            return null;

        Provider p = new Provider();
        p.setId(id);
        p.setService(url.getServiceKey());
        p.setAddress(url.getAddress());
        p.setApplication(url.getParameter(Constants.APPLICATION_KEY));
        p.setMethods(url.getParameter("methods"));  //取方法名
        p.setUrl(url.toIdentityString());
        p.setParameters(url.toParameterString());

        p.setDynamic(url.getParameter("dynamic", true));
        p.setEnabled(url.getParameter(Constants.ENABLED_KEY, true));
        p.setWeight(url.getParameter(Constants.WEIGHT_KEY, Constants.DEFAULT_WEIGHT));
        p.setUsername(url.getParameter("owner"));

        return p;
    }
    
    public static List<Provider> url2ProviderList(Map<Long, URL> ps) {
        List<Provider> ret = new ArrayList<Provider>();
        for(Map.Entry<Long, URL> entry : ps.entrySet()) {
            ret.add(url2Provider(new Pair<Long, URL>(entry.getKey(), entry.getValue())));
        }
        return ret;
    }

    public static Consumer url2Consumer(Pair<Long, URL> pair) {
    	if (pair == null) {
    		return null;
    	}
    	
        Long id = pair.getKey();
        URL url = pair.getValue();

        if (null == url)
            return null;

        Consumer c = new Consumer();
        c.setId(id);
        c.setService(url.getServiceKey());
        c.setAddress(url.getHost());
        c.setApplication(url.getParameter(Constants.APPLICATION_KEY));
        c.setParameters(url.toParameterString());

        return c;
    }
    
    public static List<Consumer> url2ConsumerList(Map<Long, URL> cs) {
        List<Consumer> list = new ArrayList<Consumer>();
        if(cs == null) return list;
        for(Map.Entry<Long, URL> entry : cs.entrySet()) {
            list.add(url2Consumer(new Pair<Long, URL>(entry.getKey(), entry.getValue())));
        }
        return list;
    }

    public static Route url2Route(Pair<Long, URL> pair) {
    	if (pair == null) {
    		return null;
    	}
    	
        Long id = pair.getKey();
        URL url = pair.getValue();

        if (null == url)
            return null;

        Route r = new Route();
        r.setId(id);
        r.setName(url.getParameter("name"));
        r.setService(url.getServiceKey());
        r.setPriority(url.getParameter(Constants.PRIORITY_KEY, 0));
        r.setEnabled(url.getParameter(Constants.ENABLED_KEY, true));
        r.setForce(url.getParameter(Constants.FORCE_KEY, false));
        r.setRule(url.getParameterAndDecoded(Constants.RULE_KEY));
        return r;
    }
    
    public static List<Route> url2RouteList(Map<Long, URL> cs) {
        List<Route> list = new ArrayList<Route>();
        if(cs == null) return list;
        for(Map.Entry<Long, URL> entry : cs.entrySet()) {
            list.add(url2Route(new Pair<Long, URL>(entry.getKey(), entry.getValue())));
        }
        return list;
    }

    /**
     * 封装成需要的override
     * 
     * @param pair 编码-服务URL
     * */
    public static com.alibaba.dubbo.registry.common.domain.Override url2Override(Pair<Long, URL> pair) {
    	if (pair == null) {
    		return null;
    	}
    	
        Long id = pair.getKey();   //序列号，编码
        URL url = pair.getValue();  //服务注册URL

        if (null == url)
            return null;

        com.alibaba.dubbo.registry.common.domain.Override o = new com.alibaba.dubbo.registry.common.domain.Override();
        o.setId(id);

        Map<String, String> parameters = new HashMap<String, String>(url.getParameters());  //服务配置参数

        o.setService(url.getServiceKey());  //服务名称
        parameters.remove(Constants.INTERFACE_KEY);
        parameters.remove(Constants.GROUP_KEY);
        parameters.remove(Constants.VERSION_KEY);
        parameters.remove(Constants.APPLICATION_KEY);
        parameters.remove(Constants.CATEGORY_KEY);
        parameters.remove(Constants.DYNAMIC_KEY);
        parameters.remove(Constants.ENABLED_KEY);

        o.setEnabled(url.getParameter(Constants.ENABLED_KEY, true));   //是否可用

        String host = url.getHost();  //主机ip
        boolean anyhost = url.getParameter(Constants.ANYHOST_VALUE, false);
        if(!anyhost || !"0.0.0.0".equals(host)) {
            o.setAddress(url.getAddress());
        }

        o.setApplication(url.getParameter(Constants.APPLICATION_KEY, url.getUsername()));
        parameters.remove(Constants.VERSION_KEY);

        o.setParams(StringUtils.toQueryString(parameters));

        return o;
    }
    
    /**
     * @param urls 加载的所有数据，包括providers，consumers，configurators等
     * */
    // Map<category, Map<servicename, Map<Long, URL>>>
    public static <SM extends Map<String, Map<Long, URL>>> Map<Long, URL> filterFromCategory(Map<String, SM> urls, Map<String, String> filter) {
        String c = (String) filter.get(Constants.CATEGORY_KEY);  //获取类别值，如configurators
        if(c==null) throw new IllegalArgumentException("no category");
        
        filter.remove(Constants.CATEGORY_KEY);
        return filterFromService(urls.get(c), filter);   //urls.get(c)获取对应类别下所有的值列表，比如configurators，会取得所有的配置服务
    }
    
    /**
     * @param cs 获取的override url列表
     * */
    public static List<com.alibaba.dubbo.registry.common.domain.Override> url2OverrideList(Map<Long, URL> cs) {
        List<com.alibaba.dubbo.registry.common.domain.Override> list = new ArrayList<com.alibaba.dubbo.registry.common.domain.Override>();
        if(cs == null) return list;
        for(Map.Entry<Long, URL> entry : cs.entrySet()) {
            list.add(url2Override(new Pair<Long, URL>(entry.getKey(), entry.getValue())));
        }
        return list;
    }
    
    
    /**
     * 过滤需要的数据
     * 
     * @param urls 获取到特定类别下的所有数据，比如configurators下对应的所有服务的配置内容
     * @param filter 设置的过滤器
     * */
    // Map<servicename, Map<Long, URL>>
    public static Map<Long, URL> filterFromService(Map<String, Map<Long, URL>> urls, Map<String, String> filter) {
        Map<Long, URL> ret = new HashMap<Long, URL>();
        if(urls == null) return ret;
        
        String s = (String) filter.remove(SERVICE_FILTER_KEY);  //.service对应的服务名称，是否是按照服务名称搜索（指定服务）
        if(s == null) {  //返回所有的数据
            for(Map.Entry<String, Map<Long, URL>> entry : urls.entrySet()) {
                filterFromUrls(entry.getValue(), ret, filter);
            }
        }
        else {  //返回指定服务的数据
            Map<Long, URL> map = urls.get(s);  //该服务对应的数据
            filterFromUrls(map, ret, filter);
        }
        
        return ret;
    }

    /**
     * @param from 特定服务对应的数据
     * @param to 接收返回的数据
     * @param filter 过滤器
     * */
    // Map<Long, URL>
    static void filterFromUrls(Map<Long, URL> from, Map<Long, URL> to, Map<String, String> filter) {
        if(from == null || from.isEmpty()) return;
        
        for(Map.Entry<Long, URL> entry : from.entrySet()) {
            URL url = entry.getValue();  //服务URL
            
            boolean match = true;
            for(Map.Entry<String, String> e : filter.entrySet()) {
                String key = e.getKey();
                String value = e.getValue();
                
                if(ADDRESS_FILTER_KEY.equals(key)) { //如果是按ip地址搜索
                    if(!value.equals(url.getAddress())) { //如果URL地址跟搜索的IP地址不匹配则跳出
                        match = false;
                        break;
                    }
                }
                else {
                    if(!value.equals(url.getParameter(key))) { //从URL参数中获取的值和过滤器中设置的值不匹配则跳出
                        match = false;
                        break;
                    }
                }
            }
            
            if(match) { //匹配，则将数据放入数据接收器
                to.put(entry.getKey(), url);
            }
        }
    }
    
    public static <SM extends Map<String, Map<Long, URL>>> Pair<Long, URL> filterFromCategory(Map<String, SM> urls, String category, Long id) {
        SM services = urls.get(category);
        if(services == null) return null;
        
        for(Map.Entry<String, Map<Long, URL>> e1 : services.entrySet()) {
            Map<Long, URL> u = e1.getValue();
            if(u.containsKey(id)) return new Pair<Long, URL>(id, u.get(id));
        }
        return null;
    }
    
    /**
     * @param cs 获取的cache url列表，并合并相同service中相同的方法（应用不同）
     * */
    public static Map<String, Cache> url2CacheList(Map<Long, URL> cs, List<Provider> providers) {
    	Map<String, Cache> cachesMap = new HashMap<String, Cache>();
        if(cs == null) return cachesMap;
        if(providers == null) return cachesMap;
        for (Provider provider : providers) {
        	for(Map.Entry<Long, URL> entry : cs.entrySet()) {
        		Map<String, Cache> map = url2Cache(new Pair<Long, URL>(entry.getKey(), entry.getValue()), provider.getMethods());
        		//合并相同service中相同的方法（应用不同）
        		for (Map.Entry<String, Cache> entry_2 : map.entrySet()) {
        			String key = entry_2.getKey();
        			Cache cache = entry_2.getValue();
					if(cachesMap.containsKey(key)) {  
						Cache cache_2 = cachesMap.get(key);
						cache_2.setOverrideId(cache_2.getOverrideId() + Constants.COMMA_SEPARATOR + cache.getOverrideId());   //用于合并统计
						cache_2.setApplication(cache_2.getApplication() + Constants.COMMA_SEPARATOR + cache.getApplication());  //用于合并展示
						cachesMap.put(key, cache_2);
					} else {
						cachesMap.put(key, cache);
					}
				}
            }
		}
        return cachesMap;
    }
    
    /**
     * 封装成需要的cache
     * 
     * @param pair 编码-服务URL
     * */
    public static Map<String, com.alibaba.dubbo.registry.common.domain.Cache> url2Cache(Pair<Long, URL> pair, String[] methods) {
    	Map<String, Cache> cacheMap = new HashMap<String, Cache>();
    	if (pair == null) {
    		return cacheMap;
    	}
    	
        Long id = pair.getKey();   //序列号，编码
        URL url = pair.getValue();  //服务注册URL

        if (null == url)
            return cacheMap;
      //一个override里可能包含多个cache配置，循环方法名匹配，获取对应cache配置
        for (String method : methods) {
        	 if(StringUtils.isNotEmpty(url.getParameter(method + ".cache"))) {
        		 Cache cache = new Cache();
             	 cache.setMethod(method);  //方法名
             	 cache.setService(url.getServiceKey());  //服务名称
                 cache.setOverrideId(id.toString());
        		 cache.setValid(true);
        		 cache.setApplication(url.getParameter(Constants.APPLICATION_KEY, url.getUsername()));  //应用名
	             cache.setCache(url.getParameter(method + ".cache", false));   //是否可用
	             cache.setExpire(url.getParameter(method + ".cache.expire", 300)); //过期时间
	             cache.setLog(url.getParameter(method + ".cache.log", true));  //是否输出 cache hit/load 日志
	             cache.setHostNull(url.getParameter(method + ".cache.hostNull", false));  //是否处理 null 对象
	             cache.setReload(url.getParameter(method + ".cache.reload", false));  //是否支持 cache 预约
	             cache.setCenter(url.getParameter(method + ".cache.center", true));  //是否用集中式cache
	             cache.setPassby(url.getParameter(method + ".cache.passby", false)); //是否重新加载 cache
	             String host = url.getHost();  //主机ip
	             boolean anyhost = url.getParameter(Constants.ANYHOST_VALUE, false);
	             if(!anyhost || !"0.0.0.0".equals(host)) {
	             	cache.setAddress(url.getAddress());
	             }
	             cacheMap.put(url.getServiceKey()+Constants.HIDE_KEY_PREFIX+method, cache);
        	 }
		}
       return cacheMap;
    }
}
